package com.zdya.combatdocuments.ethereum;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zdya.combatdocuments.component.CombatDocumentConfig;
import com.zdya.combatdocuments.util.AjaxResult;
import com.zdya.combatdocuments.util.SystemUtils;
import lombok.extern.slf4j.Slf4j;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;

import org.web3j.abi.datatypes.AbiTypes;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.ChainId;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ExecutionException;

/**
 * @Author:HYZ
 * @Date:2023-6-1
 */
@Slf4j
public class EthPeer {

    private static String dPath = "/ethereum/";

    static {
        if (SystemUtils.isWindows()) {
            if (CombatDocumentConfig.getProfile() == null) {
                dPath = "C:" + File.separator + "ethereum" + File.separator;
            } else {
                dPath = CombatDocumentConfig.getProfile() + File.separator + "ethereum" + File.separator;
            }
        }

    }

    /**
     * Error while connecting to the provider, provider not connected
     * 链
     */
    private static class Chain implements Serializable {
        String name;
        String id;
        boolean isStart;
        List<String> startList = null;
        List<String> dataList = null;
    }

    /**
     * 创世块结构
     */
    private static class GeneisBlock {
        public static class Clique {
            public final int period = 5;
            public final int epoch = 30000;
        }

        public static class Config {

            public Integer chainId = 0;
            public final int homesteadBlock = 0;
            public final int eip150Block = 0;
            public final int eip155Block = 0;
            public final int eip158Block = 0;
            public final int byzantiumBlock = 0;
            public final int constantinopleBlock = 0;
            public final int petersburgBlock = 0;
            public final int istanbulBlock = 0;
            public final int berlinBlock = 0;
            public final int londonBlock = 0;
            public final Clique clique = new Clique();

            public void setChainId(Integer chainId) {
                this.chainId = chainId;
            }
        }

        public Config config = new Config();
        //        public String coinbase = "0x0000000000000000000000000000000000000000";
        public String difficulty = "1";
        public String extraData = "";
        public String gasLimit = "800000000";
        //        public String nonce = "0x0000000000000042";
//        public final String mixhash = "0x0000000000000000000000000000000000000000000000000000000000000000";
//        public final String parentHash = "0x0000000000000000000000000000000000000000000000000000000000000000";
//        public String timestamp = "0x00";
        public HashMap alloc = new HashMap();

        public void setAlloc(HashMap list) {
            this.alloc = list;
        }

//        public void setCoinbase(String coinbase) {
//            this.coinbase = coinbase;
//        }

        public void setDifficulty(String difficulty) {
            this.difficulty = difficulty;
        }

        public void setGasLimit(String gasLimit) {
            this.gasLimit = gasLimit;
        }

        public void setExtraData(String extraData) throws UnsupportedEncodingException {
            //extraData 是0x+hexString
            // String hexData = HexUtils.toHexString( extraData.getBytes( "UTF-8" ) );

            this.extraData = extraData;
        }

        public void setConfig(Integer chainId) {
            this.config.setChainId(chainId);
        }
    }

    /**
     * 新建并且启动链
     */
    public static AjaxResult createEthereumChain(Map map) throws Exception {

        //1.配置创世块
        //必填参数：链ID、链Name、PeerName。
        //其他可选参数：预留分配金额，挖矿收益账号，难度，gaslimit

        AjaxResult ru = new AjaxResult();
        GeneisBlock geneisBlock = new GeneisBlock();
        if (map.containsKey("chainId") && map.containsKey("chainName")) {

            String chainName = map.get("chainName").toString();
            String chainId = map.get("chainId").toString();
            String peerIp = map.get("peerIp").toString();

            geneisBlock.setConfig(Integer.parseInt(chainId));
            geneisBlock.setExtraData(strTo16(chainName));
            //设置挖矿收益账号，难度，gaslimit
            if (map.containsKey("difficulty")) {
                geneisBlock.setDifficulty("0x" + new BigInteger(map.get("difficulty").toString(), 10).toString(16));
            }
            if (map.containsKey("gaslimit")) {
                geneisBlock.setGasLimit("0x" + new BigInteger(map.get("gaslimit").toString(), 10).toString(16));
            }
            if (map.containsKey("extradata")) {
                String ex = map.get("extradata").toString();
                if (ex.contains("0x")) {
                    ex = ex.substring(2, ex.length());
                }
                String extradata = "0x0000000000000000000000000000000000000000000000000000000000000000" + ex + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
                geneisBlock.setExtraData(extradata);
            }
//            String peerIp = "192.168.3.215";
            if (map.containsKey("alloc")) {
                //传过来的alloc是一个数组
                HashMap<String, String> allocList = JSON.parseObject(map.get("alloc").toString(), HashMap.class);

                HashMap<String, Map> resultList = new HashMap<String, Map>();
                for (String address : allocList.keySet()) {
                    String balance = allocList.get(address);
                    HashMap formatMap = new HashMap<String, String>();

                    //对每一个key都需要处理一下
                    formatMap.put("balance", Convert.toWei(balance, Convert.Unit.ETHER).toPlainString());
                    // formatMap.put( "balance", balance);
                    resultList.put(address, formatMap);
                }

                geneisBlock.setAlloc(resultList);
            }
            String creatName = map.get("creatName").toString();
            String address = map.get("address").toString();
            String path = dPath + chainName + "_" + creatName + File.separator + "data" + File.separator;
            String keystorePath = path + "keystore" + File.separator + address + ".keystore";
            String keystore = map.get("keystore").toString();
            String password = path + "password.txt";
            FileUtil.writeBytes(keystore.getBytes(), keystorePath);
            FileUtil.writeBytes("12345678".getBytes(), password);
            String geneisJson = JSON.toJSONString(geneisBlock);

            if (uploadGeneis(chainName, geneisJson, creatName)) {
                if (initChain(chainName, creatName)) {
                    if (startChain(chainName, chainId, address, creatName)) {
                        ru.setCode("1");
                        ru.setMessage("链启动成功");
                        //做一个启动测试
//                        String s1 = checkChainValid2(peerIp);
//                        if (s1 != null) {
//                            ru.setCode("1");
//                            ru.setMessage("链启动成功");
////                            KillPortUtils.start();
////                            FileUtil.del(path+"geth");
////                            if (initChain(chainName,creatName)) {
////                                if (startChain(chainName, chainId, address,creatName)) {
////
////                                }
////                            }
//                        } else {
//                            ru.setCode("0");
//                            ru.setMessage("链启动失败");

                    } else {
                        ru.setCode("0");
                        ru.setMessage("链执行启动命令失败");
                    }
                } else {
                    ru.setCode("0");
                    ru.setMessage("初始化链失败");
                }
            } else {
                ru.setCode("0");
                ru.setMessage("上传创世块失败");
            }

        } else {
            ru.setCode("0");
            ru.setMessage("请输入链ID和链名称");
        }
        return ru;
    }

    /**
     * 其他节点创建链不挖矿
     *
     * @param map
     * @return
     * @throws Exception
     */
    public static AjaxResult createEthereumChainNoMiner(Map map) throws Exception {

        //1.配置创世块
        //必填参数：链ID、链Name、PeerName。
        //其他可选参数：预留分配金额，挖矿收益账号，难度，gaslimit

        AjaxResult ru = new AjaxResult();
        GeneisBlock geneisBlock = new GeneisBlock();
        if (map.containsKey("chainId") && map.containsKey("chainName")) {

            String chainName = map.get("chainName").toString();
            String chainId = map.get("chainId").toString();
            String peerIp = map.get("peerIp").toString();

            geneisBlock.setConfig(Integer.parseInt(chainId));
            geneisBlock.setExtraData(strTo16(chainName));
            //设置挖矿收益账号，难度，gaslimit
            if (map.containsKey("difficulty")) {
                geneisBlock.setDifficulty("0x" + new BigInteger(map.get("difficulty").toString(), 10).toString(16));
            }
            if (map.containsKey("gaslimit")) {
                geneisBlock.setGasLimit("0x" + new BigInteger(map.get("gaslimit").toString(), 10).toString(16));
            }
            if (map.containsKey("extradata")) {
                String ex = map.get("extradata").toString();
                if (ex.contains("0x")) {
                    ex = ex.substring(2, ex.length());
                }
                String extradata = "0x0000000000000000000000000000000000000000000000000000000000000000" + ex + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
                geneisBlock.setExtraData(extradata);
            }
//            String peerIp = "192.168.3.215";
            if (map.containsKey("alloc")) {
                //传过来的alloc是一个数组
                HashMap<String, String> allocList = JSON.parseObject(map.get("alloc").toString(), HashMap.class);

                HashMap<String, Map> resultList = new HashMap<String, Map>();
                for (String address : allocList.keySet()) {
                    String balance = allocList.get(address);
                    HashMap formatMap = new HashMap<String, String>();

                    //对每一个key都需要处理一下
                    formatMap.put("balance", Convert.toWei(balance, Convert.Unit.ETHER).toPlainString());
                    // formatMap.put( "balance", balance);
                    resultList.put(address, formatMap);
                }

                geneisBlock.setAlloc(resultList);
            }

            String geneisJson = JSON.toJSONString(geneisBlock);
            String creatName = map.get("creatName").toString();
            String address = map.get("address").toString();
            String path = dPath + chainName + "_" + creatName + File.separator + "data" + File.separator;
            String keystorePath = path + "keystore" + File.separator + address + ".keystore";
            String keystore = map.get("keystore").toString();
            String password = path + "password.txt";
            FileUtil.writeBytes(keystore.getBytes(), keystorePath);
            FileUtil.writeBytes("12345678".getBytes(), password);

            if (uploadGeneis(chainName, geneisJson, map.get("creatName").toString())) {
                if (initChain(chainName, creatName)) {
                    if (startChainNoMiner(chainName, chainId, creatName)) {
                        ru.setCode("1");
                        ru.setMessage("链测试启动成功");
//                        //做一个启动测试
//                        String s1 = checkChainValid2(peerIp);
//                        if (s1 != null) {
//                            ru.setCode("1");
//                            ru.setMessage("链测试启动成功");
//                        } else {
//                            ru.setCode("0");
//                            ru.setMessage("链测试启动失败");
//                        }
                    } else {
                        ru.setCode("0");
                        ru.setMessage("链执行启动命令失败");
                    }
                } else {
                    ru.setCode("0");
                    ru.setMessage("初始化链失败");
                }
            } else {
                ru.setCode("0");
                ru.setMessage("上传创世块失败");
            }


        } else {
            ru.setCode("0");
            ru.setMessage("请输入链ID和链名称");
        }
        return ru;
    }


    /**
     * 上传创世块
     */
    private static boolean uploadGeneis(String chainName, String geneisJson, String creatName) {
        try {
//            OutputStream o = null;
//            File file = new File(dPath + chainName + "/" + "geneis.json");
//            if(!file.exists()){
//                file.mkdir();
//            }
//            o = new FileOutputStream(file.getName());
//            o.write(geneisJson.toString().getBytes("UTF-8"));
//            o.close();
            FileUtil.writeBytes(geneisJson.toString().getBytes("UTF-8"), dPath + chainName + "_" + creatName + File.separator + "geneis.json");


        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 执行链的初始化操作
     */
    private static boolean initChain(String chainName, String creatName) {
        String initCmd = "geth --datadir " + dPath + chainName + "_" + creatName + File.separator + "data init " + " " + dPath + chainName + "_" + creatName + File.separator + "geneis.json";
        try {
            boolean b = execCommand(initCmd);
            try {
                new Thread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (b) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    public static boolean execCommand(String cmd) {
        try {
            System.out.println(cmd);
            Runtime runtime = Runtime.getRuntime();
            // 打开任务管理器，exec方法调用后返回 Process 进程对象
            Process process = runtime.exec(cmd);
            // 等待进程对象执行完成，并返回“退出值”，0 为正常，其他为异常
            int exitValue = process.waitFor();
//            System.out.println("exitValue: " + exitValue);
            InputStream in = process.getInputStream();
            InputStream er = process.getErrorStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, Charset.forName("GBK")));
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(er, Charset.forName("GBK")));
            String buf = null;
            //返回数据
            StringBuilder sb = new StringBuilder();
            String errbuf;
//            while ((errbuf = errorReader.readLine()) != null) {
//                sb.append(errbuf + "\n");
//            }
//            System.out.println(sb.toString());
            sb = new StringBuilder();
            while ((errbuf = reader.readLine()) != null) {
                sb.append(errbuf + "\n");
            }
            System.out.println(sb.toString());


            // 销毁process对象
            process.destroy();
            if (exitValue == 0) {
                return true;
            } else {
                return false;
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 链启动操作
     */
    public static boolean startChain(String chainName, String chainId, String address, String creatName) {
        //TODO 启动项配置
        String startCmd = null;
//        String address = "";
        try {
            if (SystemUtils.isWindows()) {
                startCmd = "cmd /c start /b geth --datadir " + dPath + chainName + "_" + creatName + File.separator + "data" + " --syncmode full --mine  --miner.etherbase=" + address + "  --keystore " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "keystore --unlock=0  --password  " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "password.txt  --networkid " + chainId + " --maxpeers 300 --nodiscover --http --http.port=8545 --http.corsdomain \"*\" --http.addr \"0.0.0.0\" --http.api \"admin,web3,eth,txpool,miner,net\" --rpc.allow-unprotected-txs --allow-insecure-unlock 2>> geth.log &";
//                startCmd = "cmd /c start /b geth --datadir " + dPath + chainName + "_" + creatName + File.separator + "data" + " --syncmode full --mine  --miner.etherbase=" + address + "  --keystore " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "keystore --unlock=0  --password  " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "password.txt  --networkid " + chainId + " --nodiscover --http --http.port=8545 --http.corsdomain \"*\" --http.addr \"0.0.0.0\" --http.api \"admin,web3,eth,txpool,miner,net\" --rpc.allow-unprotected-txs --allow-insecure-unlock 2>> geth.log &";
            } else {
                startCmd = "nohup geth --datadir " + dPath + chainName + "_" + creatName + File.separator + "data" + " --syncmode full --mine  --miner.etherbase=" + address + "  --keystore " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "keystore --unlock=0  --password  " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "password.txt  --networkid " + chainId + " --maxpeers 300 --nodiscover --http --http.port=8545 --http.corsdomain \"*\" --http.addr \"0.0.0.0\" --http.api \"admin,web3,eth,txpool,miner,net\" --rpc.allow-unprotected-txs --allow-insecure-unlock >/logs/geth.log 2>&1 &";
            }
            System.out.println(startCmd);
            Runtime runtime = Runtime.getRuntime();
// 打开任务管理器，exec方法调用后返回 Process 进程对象
            Process process = runtime.exec(startCmd);

            try {
                new Thread().sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 链启动操作
     */
    public static boolean startChainNoMiner(String chainName, String chainId, String creatName) {
        //TODO 启动项配置
        String startCmd = null;
//        String address = "";
        try {
            if (SystemUtils.isWindows()) {

                startCmd = "cmd /c start /b geth --datadir " + dPath + chainName + "_" + creatName + File.separator + "data" + " --syncmode full --keystore " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "keystore --unlock=0  --password  " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "password.txt  --networkid " + chainId + " --nodiscover --http --http.port=8545 --http.corsdomain \"*\" --http.addr \"0.0.0.0\" --http.api \"admin,web3,eth,txpool,miner,net\" --rpc.allow-unprotected-txs --allow-insecure-unlock 2>> geth.log &";
//                startCmd = "cmd /c start /b geth --datadir " + dPath + chainName + "/data" + " --networkid " + chainId + " --identity " + chainId + " --nodiscover --rpccorsdomain \"*\" --rpc --rpcaddr \"0.0.0.0\" --rpcapi \"eth,web3,admin,miner,net,personal\" --allow-insecure-unlock 2>> " + chainName + ".log &";
            } else {
                startCmd = "nohup geth --datadir " + dPath + chainName + "_" + creatName + File.separator + "data" + " --syncmode full --keystore " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "keystore --unlock=0  --password  " + dPath + chainName + "_" + creatName + File.separator + "data" + File.separator + "password.txt  --networkid " + chainId + " --nodiscover --http --http.port=8545 --http.corsdomain \"*\" --http.addr \"0.0.0.0\" --http.api \"admin,web3,eth,txpool,miner,net\" --rpc.allow-unprotected-txs --allow-insecure-unlock >/logs/geth.log 2>&1 &";
            }
            System.out.println(startCmd);
            Runtime runtime = Runtime.getRuntime();
// 打开任务管理器，exec方法调用后返回 Process 进程对象
            Process process = runtime.exec(startCmd);

            try {
                new Thread().sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 执行单条指令
     */
    //关闭节点命令"sh /ethereum/stop.sh"
    private static boolean execCmd(String command) throws Exception {
        System.out.println(command);
        try {
            String s = RuntimeUtil.execForStr(command);
            System.out.println(s);
        } catch (IORuntimeException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 开启节点挖矿
     */
    public static AjaxResult startMine(String address) throws Exception {
        AjaxResult ru = new AjaxResult();

        // String peerAddress = peerList.get( map.get( "peerName" ).toString() );

        String peerIp = "127.0.0.1";
        //code -32000,必须指定节点挖矿收益账号。
        String url = "http://" + peerIp + ":8545";
        Map map = new HashMap<>();
        List list = new ArrayList();
        list.add(address);
        map.put("jsonrpc", "2.0");
        map.put("method", "miner_setEtherbase");
        map.put("params", list);
        map.put("id", "66");
        String s = JSONUtil.toJsonStr(map);


        String result = doPostOrGet(url, s);
        if (result != null) {
            Map hashMap = JSONUtil.parseObj(result);

            if (hashMap.get("error") == null) {
                log.info("开启挖矿请求jsonRPC接口返回数据：{}", result);
                ru.setMessage(result);
                ru.setCode("1");
            } else {
                Map errorMap = JSONUtil.parseObj(hashMap.get("error"));
                if ("-32000".equals(errorMap.get("code"))) {
                    ru.setCode("0");
                    ru.setMessage("设置矿工账户失败");
                    return ru;
                }
            }

        }
        result = doPostOrGet(url, "{\"jsonrpc\":\"2.0\",\"method\":\"miner_start\",\"params\":[2],\"id\":67}");
        if (result != null) {
            Map hashMap = JSONUtil.parseObj(result);
            if (hashMap.get("error") == null) {
                log.info("开启挖矿请求jsonRPC接口返回数据：{}", result);
                ru.setMessage(result);
                ru.setCode("1");
            } else {
                Map errorMap = JSONUtil.parseObj(hashMap.get("error"));
                if ("-32000".equals(errorMap.get("code"))) {
                    ru.setCode("0");
                    ru.setMessage("链上没有预留账户");
                }
            }
        } else {
            ru.setCode("0");
            ru.setMessage("链处于关闭状态");
        }
        return ru;
    }

    /**
     * 关闭节点挖矿
     */
    public static AjaxResult stopMine() throws Exception {
        AjaxResult ru = new AjaxResult();
        String peerIp = "127.0.0.1";
        // code -32000,必须指定节点挖矿收益账号。
        String address = "http://" + peerIp + ":8545";
        String result = doPostOrGet(address, "{\"jsonrpc\":\"2.0\",\"method\":\"miner_stop\",\"params\":[],\"id\":67}");
        if (result != null) {
            HashMap hashMap = JSON.parseObject(result, HashMap.class);
            ru.setCode("1");
            //成功还是失败
            ru.setMessage(result);
            log.info("停止挖矿请求jsonRPC接口返回数据：{}", result);
        } else {
            ru.setCode("0");
            ru.setMessage("链处于关闭状态");
        }
        return ru;
    }

    /**
     * 指定挖矿收益地址
     */
    public AjaxResult setEtherbase(Map map) throws Exception {
        AjaxResult ru = new AjaxResult();
        String peerIp = "127.0.0.1";
        String coinbase = map.get("address").toString();
        String note = map.get("note").toString();
        List addresss = new ArrayList();
        addresss.add(coinbase);
        String address = "http://" + peerIp + ":8545";
        String result = doPostOrGet(address, "{\"jsonrpc\":\"2.0\",\"method\":\"miner_setEtherbase\",\"params\":[\"" + coinbase + "\"],\"id\":67}");
        String result2 = doPostOrGet(address, "{\"jsonrpc\":\"2.0\",\"method\":\"miner_setExtra\",\"params\":[\"" + note + "\"],\"id\":67}");
        if (result != null && result2 != null) {
            HashMap hashMap = JSON.parseObject(result, HashMap.class);
            if (hashMap.get("result") != null && (boolean) hashMap.get("result")) {
                ru.setCode("1");
                ru.setMessage(result);
            } else {
                ru.setCode("0");
                ru.setMessage(hashMap.toString());
            }
        } else {
            ru.setCode("0");
            ru.setMessage("链处于关闭状态");
        }
        return ru;
    }


    /**
     * 合约部署
     * 需要用户 address，私钥，合约bytecode,合约构造参数的变量值list.
     */
    public static AjaxResult deployContract(String chainId, String address, String keystore, String reqGasLimit, String reqGasPrice, String byteCode, String arr, String contract_name) throws Exception {

        AjaxResult ru = new AjaxResult();
        //需要传 bytecode 和 构造参数的列表
        if (StringUtils.isBlank(chainId)
                || StringUtils.isBlank(address)
                || StringUtils.isBlank(byteCode)
                || StringUtils.isBlank(contract_name)) {
            ru.setCode("0");
            ru.setMessage("合约部署失败");
        }

        EthWallet.EthHDWallet wallet = EthWallet.decryptWalletByKeystore(keystore);
        String privateKey = "";

        privateKey = wallet.privateKey;


        Web3j web3jUtil = Web3j.build(new HttpService("http://127.0.0.1:8545"));
        if (web3jUtil == null) {
            ru.setCode("0");
            ru.setMessage("节点配置有误");
        }


//        String arr = String.valueOf(map.get("arr"));
        List inputParameters = new ArrayList<>();

        boolean flag = false;
        try {
            if (StringUtils.isNotBlank(arr) && !"null".equals(arr)) {
                JSONArray arrJsonArray = JSONUtil.parseArray(arr);
                for (int i = 0; i < arrJsonArray.size(); i++) {
                    JSONObject arrJsonObject = JSONUtil.parseObj(arrJsonArray.get(i));
                    if (arrJsonObject.get("type") != null && !"".equals(arrJsonObject.get("type")) && !"null".equals(arrJsonObject.get("type"))) {
                        if ("bool".equals(String.valueOf(arrJsonObject.get("type")))) {
                            inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType(String.valueOf(arrJsonObject.get("type"))), Boolean.valueOf(String.valueOf(arrJsonObject.get("value")))));
                        } else if (String.valueOf(arrJsonObject.get("type")).indexOf("int") != -1) {
                            if ("uint".equals(arrJsonObject.get("type"))) {
                                inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType("uint256"), Long.valueOf(String.valueOf(arrJsonObject.get("value")))));
                            } else {
                                inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType(String.valueOf(arrJsonObject.get("type"))), Long.valueOf(String.valueOf(arrJsonObject.get("value")))));
                            }
                        } else {
                            inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType(String.valueOf(arrJsonObject.get("type"))), arrJsonObject.get("value")));
                        }
                    }


                }
            }
        } catch (Exception e) {
            flag = true;
            e.printStackTrace();
        }


        String encodedConstructor = FunctionEncoder.encodeConstructor(inputParameters);

        //using a raw transaction
        encodedConstructor = "";
        BigInteger nonce = getTransactionNonce(address, web3jUtil);

        //gasprice-2Gwei
        BigInteger gasPrice = Convert.toWei(BigDecimal.valueOf(8), Convert.Unit.GWEI).toBigInteger();

        //todo1
        BigInteger gasLimit = BigInteger.valueOf(220000);


        if (StringUtils.isNotBlank(reqGasLimit) && !"null".equals(reqGasLimit)) {
            gasLimit = new BigInteger(reqGasLimit.toString());
        }
        // gasLimit = Contract.GAS_LIMIT;
        //gasprice-如果没有传入gasprice就自动获取

        if (StringUtils.isNotBlank(reqGasPrice) && !"null".equals(reqGasPrice)) {
            gasPrice = Convert.toWei(new BigDecimal(reqGasPrice), Convert.Unit.GWEI).toBigInteger();
        }

        String value = "0";

        BigInteger weiValue = Convert.toWei(new BigDecimal(value), Convert.Unit.ETHER).toBigInteger();

        //判断转账方是否余额足够
//        BigInteger balance = web3jUtil.ethGetBalance(address, DefaultBlockParameter.valueOf("Latest")).send().getBalance();
//        //两个wei 进行比较
//        if (weiValue.compareTo(balance) == 1) {
//            //转账金额大于余额
//            ru.setMessage("余额不足");
//            return ru;
//        }
//
//        Transaction contractTransaction = Transaction.createContractTransaction(address, nonce, gasPrice, gasLimit, weiValue, "0x" + byteCode + encodedConstructor);
//
//        web3jUtil.ethSendTransaction(contractTransaction).
        //2.对合约创建交易进行签名
        RawTransaction rawTransaction = RawTransaction.createContractTransaction(nonce, gasPrice, gasLimit, weiValue, "0x" + byteCode + encodedConstructor);

        // send...
        if (privateKey.startsWith("0x")) {
            privateKey = privateKey.substring(2);
        }
        ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16));
        Credentials credentials = Credentials.create(ecKeyPair);

        byte[] signedMessage = null;

        Long bchainId = Long.valueOf(chainId);
//        byte bchainId = (byte) (Integer.parseInt(chainId)+1);

//        signedMessage = TransactionEncoder.signMessage(rawTransaction, bchainId, credentials);
        //如果是合理的chainId 对chainId进行签名
        if (bchainId > ChainId.NONE) {

            signedMessage = TransactionEncoder.signMessage(rawTransaction, bchainId, credentials);
        } else {
            signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        }

        String signedData = Numeric.toHexString(signedMessage);


        //3.发送签完名的交易,如果签名成功的话
        if (signedData != null) {
            //发送交易
            EthSendTransaction ethSendTransaction = null;
            try {
                ethSendTransaction = web3jUtil.ethSendRawTransaction(signedData).send();

                if (ethSendTransaction.hasError()) {
                    ru.setCode("0");
                    ru.setMessage("交易失败:" + ethSendTransaction.getError().getMessage());
                } else {
                    ru.setCode("1");
                    String transactionHash = ethSendTransaction.getTransactionHash();
//                    ethSendTransaction

                    String contractAddress = getContractAddress(web3jUtil, transactionHash);

//                    ru.setMessage("交易hash:" + "" + transactionHash);
                    ru.setMessage(contractAddress);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
                ru.setCode("0");
            } catch (ExecutionException e) {
                e.printStackTrace();
                ru.setCode("0");
            }

            // if (ethSendTransaction.hasError()) {
            //     //交易发送失败
            // } else {
            //     //交易发送成功了
            //     String transactionHash = ethSendTransaction.getTransactionHash();
            //     //4.查询交易收据，后去合约地址，查看合约是否部署成功
            //     EthGetTransactionReceipt transactionReceipt =
            //             web3jUtil.ethGetTransactionReceipt(transactionHash).send();
            //
            //     if (transactionReceipt.getTransactionReceipt().isPresent()) {
            //         //获取到了部署成功的合约地址
            //         String contractAddress = transactionReceipt.getTransactionReceipt().get().getContractAddress();
            //     } else {
            //         // try again
            //         //部署失败，或者链上没人挖矿，需要开启挖矿，或者过一会儿就部署成功了
            //     }
            // }
        }

        return ru;
    }

    public static String getContractAddress(Web3j web3j, String hash) throws Exception {
        log.info("获取合约地址！");
        EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt(hash).send();
        if (transactionReceipt.getResult() != null) {
            String contractAddress = transactionReceipt.getTransactionReceipt().get().getContractAddress();
            return contractAddress;
        } else {
            Thread.sleep(1000l);
            return getContractAddress(web3j, hash);
        }
    }


    /*调用合约*/
    public static AjaxResult ethCallContract(String chainId,
                                             String type,
                                             String contractAddress,
                                             String functionName,
                                             String arrString) throws Exception {

        AjaxResult ru = new AjaxResult();
        //1.取参数
        if (StringUtils.isBlank(chainId)
                || StringUtils.isBlank(type)
                || StringUtils.isBlank(contractAddress)
                || "null".equals(chainId)
                || "null".equals(type)
                || "null".equals(contractAddress)
        ) {
            ru.setCode("0");
            ru.setMessage("合约调用有误");
        }
        log.info("上链数据：" + arrString);
//        String arrString = String.valueOf(map.get("arr"));

        Web3j web3jUtil = Web3j.build(new HttpService("http://127.0.0.1:8545"));

        List inputParameters = new ArrayList<>();
        boolean flag = false;
        try {
            if (StringUtils.isNotBlank(arrString) && !"null".equals(arrString)) {
                JSONArray arrJsonArray = JSONUtil.parseArray(arrString);
                for (int i = 0; i < arrJsonArray.size(); i++) {
                    JSONObject arrJsonObject = JSONUtil.parseObj(arrJsonArray.get(i));
                    if (arrJsonObject.get("type") != null && !"".equals(arrJsonObject.get("type")) && !"null".equals(arrJsonObject.get("type"))) {
                        if ("bool".equals(String.valueOf(arrJsonObject.get("type")))) {
                            inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType(String.valueOf(arrJsonObject.get("type"))), Boolean.valueOf(String.valueOf(arrJsonObject.get("value")))));
                        } else if (String.valueOf(arrJsonObject.get("type")).indexOf("int") != -1) {
                            if ("uint".equals(arrJsonObject.get("type"))) {
                                inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType("uint256"), Long.valueOf(String.valueOf(arrJsonObject.get("value")))));
                            } else {
                                inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType(String.valueOf(arrJsonObject.get("type"))), Long.valueOf(String.valueOf(arrJsonObject.get("value")))));
                            }
                        } else {
                            inputParameters.add(ReflectUtil.newInstance(AbiTypes.getType(String.valueOf(arrJsonObject.get("type"))), arrJsonObject.get("value")));
                        }
                    }


                }
            }
        } catch (Exception e) {
            flag = true;
            e.printStackTrace();
        }

        if (flag) {
            ru.setCode("0");
            ru.setMessage("合约调用失败:");
            return ru;
        }

        List<TypeReference<?>> outputParameters = new ArrayList<>();
        TypeReference<Bool> typeReference = new TypeReference<Bool>() {
        };
        outputParameters.add(typeReference);

        String address = "0xb80377b5e52a16b317fd5f61e10ec0d1e8ee05e3";
        String keystore = "{\"address\":\"b80377b5e52a16b317fd5f61e10ec0d1e8ee05e3\",\"id\":\"656a6015-6c53-4357-bc3b-e962d9e67879\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"ciphertext\":\"06607e3e0128636c7bf9e0d8d518f451d4d5f3ed3f0f891e3d049236fc0df68f\",\"cipherparams\":{\"iv\":\"a42f13ef35debcbe81e266889a546c38\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":4096,\"p\":6,\"r\":8,\"salt\":\"a6020a1bd769e724e0113f108f7278b70eeca952e4e3887adae7bc2bc1ac5d1c\"},\"mac\":\"7959347fd7f15b93c2c65e49946074341dd52182a80035f54ba56e3c851a952c\"}}";


        if ("0".equals(type)) {

            EthWallet.EthHDWallet wallet = EthWallet.decryptWalletByKeystore(keystore);
            String privateKey = "";

            privateKey = wallet.privateKey;

            //nonce
            BigInteger nonce = getTransactionNonce(address, web3jUtil);
            //gaslimit,默认是21000
            BigInteger gasLimit = BigInteger.valueOf(30000000);
            //value
            String value = "0";
//            String reqValue = String.valueOf(map.get("value"));
//            if (StringUtils.isNotBlank(reqValue) && !"null".equals(reqValue)) {
//                value = reqValue;
//            }
            BigInteger weiValue = Convert.toWei(new BigDecimal(value), Convert.Unit.ETHER).toBigInteger();


            //判断转账方是否余额足够
//            BigInteger balance = web3jUtil.ethGetBalance(address, DefaultBlockParameter.valueOf("Latest")).send().getBalance();
//            //两个wei 进行比较
//            if (weiValue.compareTo(balance) == 1) {
//                //转账金额大于余额
//                ru.setMessage("余额不足");
//                return ru;
//            }

            // gasLimit = Contract.GAS_LIMIT;
            //gasprice-如果没有传入gasprice就自动获取
            BigInteger gasPrice = Convert.toWei(BigDecimal.valueOf(2), Convert.Unit.GWEI).toBigInteger();

            Function function = new Function(functionName, inputParameters, Collections.emptyList());
            String data = FunctionEncoder.encode(function);
            // data = "0xc6888fa10000000000000000000000000000000000000000000000000000000000000007";
            //对交易进行签名
            String signedData = null;
            Long bchainId = Long.valueOf(chainId);

            signedData = signTransaction(nonce, gasPrice, gasLimit, contractAddress, weiValue, data, bchainId, privateKey);

            //同步异步
            if (signedData != null) {
                //发送交易
                EthSendTransaction ethSendTransaction = null;
                try {
                    ethSendTransaction = web3jUtil.ethSendRawTransaction(signedData).sendAsync().get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                if (ethSendTransaction.hasError()) {
                    ru.setCode("0");
                    ru.setMessage("合约调用失败:" + ethSendTransaction.getError().getMessage());
                } else {
                    ru.setCode("1");
                    String transactionHash = ethSendTransaction.getTransactionHash();
                    ru.setMessage("调用合约hash:" + "" + transactionHash);
                    ru.setMessage(transactionHash);
                }


            } else {
                ru.setCode("0");
                ru.setMessage("调用合约签名失败");
            }
            // contractInteract(web3jUtil,functionName, null, address, nonce, gasPrice, gasLimit, contractAddress, weiValue);
        } else if ("1".equals(type)) {
            // contractCall(web3jUtil,functionName, address, contractAddress,inputParameters,new ArrayList());
//            EthWallet.unlockWallet("127.0.0.1", address, "12345678");
            Function function = new Function(functionName, inputParameters, outputParameters);

            String encodedFunction = FunctionEncoder.encode(function);

            Transaction transaction = Transaction.createEthCallTransaction(address, contractAddress, encodedFunction);

            org.web3j.protocol.core.methods.response.EthCall response = web3jUtil.ethCall(transaction, DefaultBlockParameterName.LATEST).sendAsync().get();

            if (response == null || response.getValue() == null || "".equals(response.getValue()) || "0x".equals(response.getValue())) {
                ru.setCode("0");
                ru.setMessage("合约调用失败:");
            } else {
                ru.setCode("1");
                ru.setMessage(response.getValue());
            }
            // List<Type> someTypes = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters());
        } else {
            ru.setCode("0");
            ru.setMessage("方法类型有误");
        }

        return ru;
    }

    /**
     * 获取账号交易次数 nonce
     *
     * @param address 钱包地址
     * @return nonce
     */
    private static BigInteger getTransactionNonce(String address, Web3j web3jUtil) {
        BigInteger nonce = BigInteger.ZERO;
        try {
            EthGetTransactionCount ethGetTransactionCount = web3jUtil.ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).send();
            nonce = ethGetTransactionCount.getTransactionCount();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return nonce;
    }

    /**
     * 签名交易
     */
    public static String signTransaction(BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to,
                                         BigInteger value, String data, Long chainId, String privateKey) throws IOException {
        byte[] signedMessage;
        RawTransaction rawTransaction = RawTransaction.createTransaction(
                nonce,
                gasPrice,
                gasLimit,
                to,
                value,
                data);
        if (privateKey.startsWith("0x")) {
            privateKey = privateKey.substring(2);
        }
        ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16));
        Credentials credentials = Credentials.create(ecKeyPair);

        //如果是合理的chainId 对chainId进行签名
        if (chainId > ChainId.NONE) {

            signedMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
        } else {
            signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        }


        String hexValue = Numeric.toHexString(signedMessage);
        return hexValue;
    }


    /**
     * java Http
     */
    private static String doPostOrGet(String pathUrl, String data) {
        log.info("向节点发送请求：" + data);
        OutputStreamWriter out = null;
        BufferedReader br = null;
        String result = "";
        try {
            URL url = new URL(pathUrl);
            //打开和url之间的连接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //请求方式
            conn.setRequestMethod("POST");
            //conn.setRequestMethod("GET");

            //设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
            conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");

            //DoOutput设置是否向httpUrlConnection输出，DoInput设置是否从httpUrlConnection读入，此外发送post请求必须设置这两个
            conn.setDoOutput(true);
            conn.setDoInput(true);

            /**
             * 下面的三句代码，就是调用第三方http接口
             */
            //获取URLConnection对象对应的输出流
            out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
            //发送请求参数即数据
            out.write(data);
            //flush输出流的缓冲
            out.flush();

            /**
             * 下面的代码相当于，获取调用第三方http接口后返回的结果
             */
            //获取URLConnection对象对应的输入流
            InputStream is = conn.getInputStream();
            //构造一个字符流缓存
            br = new BufferedReader(new InputStreamReader(is));
            String str = "";
            while ((str = br.readLine()) != null) {
                result += str;
            }
            // System.out.println(result);
            //关闭流
            is.close();
            //断开连接，disconnect是在底层tcp socket链接空闲时才切断，如果正在被其他线程使用就不切断。
            conn.disconnect();

            return result;

        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * 检测节点状态,不检测挖矿状态
     */
    /*public static String checkChainValid2(String peerAddress) {
        String result = null;
        String address = "http://" + peerAddress + ":8545";
        Web3j web3j = Web3j.build(new HttpService(address));

//        Admin admin = Admin.build(new HttpService(address));
        try {
            //extraData是建链时 记录的链名称
            String extraData = web3j.ethGetBlockByNumber(new DefaultBlockParameterNumber(0), false).send().getBlock().getExtraData();
            result = hexStringToString(extraData.substring(2));

        } catch (Throwable e) {
            e.printStackTrace();
        }
        return result;
    }*/
    private static String hexStringToString(String s) {
        if (s == null || s.equals("")) {
            return null;
        }
        s = s.replace(" ", "");
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(
                        s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "UTF-8");
            new String();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;


    }

    public static String strTo16(String s) {
        String str = "";
        for (int i = 0; i < s.length(); i++) {
            int ch = (int) s.charAt(i);
            String s4 = Integer.toHexString(ch);
            str = str + s4;
        }
        return "0x" + str;
    }
}
